

#include <QtGui/QtGui>
#include "MapExtractor.h"
#include "QLogger.h"

#ifdef __cplusplus
extern "C" {
#endif
#include "JPEG.H"
#include "JPEGINT.H"
#ifdef __cplusplus
}
#endif


//--------------------------------------------------------------------------------------------------
struct UnitHeader
{
    uint    flag;   // 单元头标识
    uint    size;   // 除去单元头的单元大小
};

enum MapType
{
    MAP_MAPX = 0x4D415058,      // MAPX
    MAP_M1_0 = 0x4D312E30,      // M1.0
    MAP_M2_5 = 0x4D322E35,      // M2.5
    MAP_M3_0 = 0x4D332E30,      // M3.0
    MAP_ROL0 = 0x524F4C30,      // ROL0
};

enum UnitFlag
{
    UNIT_IMAG = 0x494D4147,     // IMAG
    UNIT_MSK2 = 0x4D534B32,     // MSK2
    UNIT_MASK = 0x4D41534B,     // MASK
    UNIT_JPEG = 0x4A504547,     // JPEG
    UNIT_BLOK = 0x424C4F4B,     // BLOK
    UNIT_CELL = 0x43454C4C,     // CELL
    UNIT_BRIG = 0x42524947,     // BRIG
    UNIT_LIGT = 0x4C494754,     // LIGT
    UNIT_END  = 0x454E4420,     // END
};


//--------------------------------------------------------------------------------------------------

const ushort RGB555_R_MASK = 0x7C00;
const ushort RGB555_G_MASK = 0x03E0;
const ushort RGB555_B_MASK = 0x001F;


inline uint R5G5B5_RGB(ushort color)
{
    return qRgb((uchar)(((color&RGB555_R_MASK)>>10)<<3),
        (uchar)(((color&RGB555_G_MASK)>>5)<<3),
        (uchar)(color&RGB555_B_MASK)<<3);
}

static int DecompressMask(void* in , void* out)
{
    register uchar *op;
    register  uchar *ip;
    register unsigned t;
    register  uchar *m_pos;

    op = (uchar *) out;
    ip = (uchar *) in;

    if (*ip > 17) {
        t = *ip++ - 17;
        if (t < 4)
            goto match_next;
        do *op++ = *ip++; while (--t > 0);
        goto first_literal_run;
    }

    while (1) {
        t = *ip++;
        if (t >= 16) goto match;
        if (t == 0) {
            while (*ip == 0) {
                t += 255;
                ip++;
            }
            t += 15 + *ip++;
        }

        * (unsigned *) op = * ( unsigned *) ip;
        op += 4; ip += 4;
        if (--t > 0)
        {
            if (t >= 4)
            {
                do {
                    * (unsigned * ) op = * ( unsigned * ) ip;
                    op += 4; ip += 4; t -= 4;
                } while (t >= 4);
                if (t > 0) do *op++ = *ip++; while (--t > 0);
            }
            else do *op++ = *ip++; while (--t > 0);
        }

first_literal_run:

        t = *ip++;
        if (t >= 16)
            goto match;

        m_pos = op - 0x0801;
        m_pos -= t >> 2;
        m_pos -= *ip++ << 2;

        *op++ = *m_pos++; *op++ = *m_pos++; *op++ = *m_pos;

        goto match_done;

        while (1)
        {
match:
            if (t >= 64)
            {

                m_pos = op - 1;
                m_pos -= (t >> 2) & 7;
                m_pos -= *ip++ << 3;
                t = (t >> 5) - 1;

                goto copy_match;

            }
            else if (t >= 32)
            {
                t &= 31;
                if (t == 0)	{
                    while (*ip == 0) {
                        t += 255;
                        ip++;
                    }
                    t += 31 + *ip++;
                }

                m_pos = op - 1;
                m_pos -= (* ( unsigned short * ) ip) >> 2;
                ip += 2;
            }
            else if (t >= 16) {
                m_pos = op;
                m_pos -= (t & 8) << 11;
                t &= 7;
                if (t == 0) {
                    while (*ip == 0) {
                        t += 255;
                        ip++;
                    }
                    t += 7 + *ip++;
                }
                m_pos -= (* ( unsigned short *) ip) >> 2;
                ip += 2;
                if (m_pos == op)
                    goto eof_found;
                m_pos -= 0x4000;
            }
            else {
                m_pos = op - 1;
                m_pos -= t >> 2;
                m_pos -= *ip++ << 2;
                *op++ = *m_pos++; *op++ = *m_pos;
                goto match_done;
            }

            if (t >= 6 && (op - m_pos) >= 4) {
                * (unsigned *) op = * ( unsigned *) m_pos;
                op += 4; m_pos += 4; t -= 2;
                do {
                    * (unsigned *) op = * ( unsigned *) m_pos;
                    op += 4; m_pos += 4; t -= 4;
                } while (t >= 4);
                if (t > 0) do *op++ = *m_pos++; while (--t > 0);
            }
            else {
copy_match:
                *op++ = *m_pos++; *op++ = *m_pos++;
                do *op++ = *m_pos++; while (--t > 0);
            }

match_done:

            t = ip[-2] & 3;
            if (t == 0)	break;

match_next:
            do *op++ = *ip++; while (--t > 0);
            t = *ip++;
        }
    }

eof_found:
    //   if (ip != ip_end) return -1;
    return (op - (uchar*)out);
}


//--------------------------------------------------------------------------------------------------

MapExtractor::MapExtractor(QObject *parent /* = 0 */)
    : QThread(parent)
    , mInputPath("")
    , mOutputPath("")
    , mTerminate(false)
{

}

MapExtractor::~MapExtractor()
{

}

//--------------------------------------------------------------------------------------------------

bool MapExtractor::run(const QString &inputPath, const QString &outputPath, const QString &imagePath, bool async /* = true */)
{
    bool result = false;

    mInputPath = inputPath;
    mOutputPath = outputPath;
    mImagePath = imagePath;

    if (async)
    {
        mTerminate = false;
        start();
        result = true;
    }
    else
    {
        result = extract();
    }

    return result;
}

void MapExtractor::stop()
{
    if (isRunning())
    {
        mTerminate = true;
        wait();
    }
}

//--------------------------------------------------------------------------------------------------

bool MapExtractor::extract()
{
    bool result = false;

    init_jpeg();

    mGameType = extractGameType();

    switch (mGameType)
    {
    case GAME_XY2:
        result = extractXY2Map();
        break;
    case GAME_XY3:
        result = extractXY3Map();
        break;
    case GAME_XYQ:
        result = extractXYQMap();
        break;
    case GAME_XYW:
        result = extractXYWMap();
        break;
    default:
        break;
    }

    if (result)
    {
        notifyCompleted(true, 0, "Mission completed !!!");
    }

    return result;
}

// 提取游戏类型
int MapExtractor::extractGameType()
{
    QString fileName = mInputPath + "update.ini";
    QSettings settings(fileName, QSettings::IniFormat);
    settings.beginGroup("Setting");
    QString value = settings.value("ServerList").toString();
    settings.endGroup();;
    int pos = value.indexOf("xy");
    QString tag = value.mid(pos, 3);
    int type = GAME_UNKNOWN;

    if ("xy." == tag)
    {
        type = GAME_XY2;
    }
    else if ("xy3" == tag)
    {
        type = GAME_XY3;
    }
    else if ("xyq" == tag)
    {
        type = GAME_XYQ;
    }
    else if ("xyw" == tag)
    {
        type = GAME_XYW;
    }

    return type;
}

// 提取大话西游2地图
bool MapExtractor::extractXY2Map()
{
    bool result = false;

    QStringList filters;
    filters<<"*.map";

    // 新版地图
    QString newScenePath = mInputPath + "newscene/";
    QDir newDir(newScenePath);
    QFileInfoList newSceneList = newDir.entryInfoList(filters, QDir::Files|QDir::NoSymLinks|QDir::NoDotAndDotDot, QDir::Name);

    // 老版地图
    QString scenePath = mInputPath + "scene/";
    QDir sceneDir(scenePath);
    QFileInfoList sceneList = sceneDir.entryInfoList(filters, QDir::Files|QDir::NoSymLinks|QDir::NoDotAndDotDot, QDir::Name);

    int total = newSceneList.size() + sceneList.size();
    int pos = 0;
    notifyStarted(total);

    QString outputPath = mOutputPath;
    mOutputPath = outputPath + "scene/";

    // 处理老版地图
    QFileInfoList::const_iterator itr = sceneList.constBegin();
    while (itr != sceneList.constEnd() && !mTerminate)
    {
        // 通知进度
        notifyProgress(pos);

        const QFileInfo &info = *itr;
        const QString &filePath = info.filePath();
        const QString &fileName = info.fileName();

        // 通知详情描述
        QString msg = tr("Extracting map file : ") + fileName;
        notifyDetail(msg);

        // 提取当前地图文件
        result = extractOneMap(filePath);
        if (!result)
            break;

        ++pos;
        ++itr;
    }

//    return result;

    mOutputPath = outputPath + "newscene/";

    // 处理新版地图
    itr = newSceneList.constBegin();
    while (itr != newSceneList.constEnd() && !mTerminate)
    {
        // 通知进度
        notifyProgress(pos);

        const QFileInfo &info = *itr;
        const QString &filePath = info.filePath();
        const QString &fileName = info.fileName();

        // 通知详情描述
        QString msg = tr("Extracting map file : ") + fileName;
        notifyDetail(msg);

        // 提取当前地图文件
        result = extractOneMap(filePath);
        if (!result)
            break;

        ++pos;
        ++itr;
    }

    mOutputPath = outputPath;

    return result && !mTerminate;
}

// 提取大话西游3地图
bool MapExtractor::extractXY3Map()
{
    bool result = false;
    QStringList filters;
    filters<<"*.map";

    // 新版地图
    QString mapPath = mInputPath + "map/";
    QDir dir(mapPath);
    QFileInfoList mapPathList = dir.entryInfoList(filters, QDir::Files|QDir::NoSymLinks|QDir::NoDotAndDotDot, QDir::Name);

    int total = mapPathList.size();

    notifyStarted(total);

    // 处理新版地图
    int pos = 0;
    QFileInfoList::const_iterator itr = mapPathList.constBegin();
    while (itr != mapPathList.constEnd() && !mTerminate)
    {
        // 通知进度
        notifyProgress(pos);

        const QFileInfo &info = *itr;
        const QString &filePath = info.filePath();
        const QString &fileName = info.fileName();

        // 通知详情描述
        QString msg = tr("Extracting map file : ") + fileName;
        notifyDetail(msg);

        // 提取当前地图文件
        result = extractOneMap(filePath);
        if (!result)
            break;

        ++pos;
        ++itr;
    }

    return result && !mTerminate;
}

// 提取梦幻西游地图
bool MapExtractor::extractXYQMap()
{
    bool result = false;
    QStringList filters;
    filters<<"*.map";

    // 新版地图
    QString scenePath = mInputPath + "scene/";
    QDir dir(scenePath);
    QFileInfoList scenePathList = dir.entryInfoList(filters, QDir::Files|QDir::NoSymLinks|QDir::NoDotAndDotDot, QDir::Name);

    int total = scenePathList.size();

    notifyStarted(total);

    // 处理新版地图
    int pos = 0;
    QFileInfoList::const_iterator itr = scenePathList.constBegin();
    while (itr != scenePathList.constEnd() && !mTerminate)
    {
        // 通知进度
        notifyProgress(pos);

        const QFileInfo &info = *itr;
        const QString &filePath = info.filePath();
        const QString &fileName = info.fileName();

        // 通知详情描述
        QString msg = tr("Extracting map file : ") + fileName;
        notifyDetail(msg);

        // 提取当前地图文件
        result = extractOneMap(filePath);
        if (!result)
            break;

        ++pos;
        ++itr;
    }

    return result && !mTerminate;
}

// 提取大话外传地图
bool MapExtractor::extractXYWMap()
{
    bool result = false;
    QStringList filters;
    filters<<"*.map";

    // 新版地图
    QString mapPath = mInputPath + "map/";
    QDir dir(mapPath);
    QFileInfoList mapPathList = dir.entryInfoList(filters, QDir::Files|QDir::NoSymLinks|QDir::NoDotAndDotDot, QDir::Name);

    int total = mapPathList.size();

    notifyStarted(total);

    // 处理新版地图
    int pos = 0;
    QFileInfoList::const_iterator itr = mapPathList.constBegin();
    while (itr != mapPathList.constEnd() && !mTerminate)
    {
        // 通知进度
        notifyProgress(pos);

        const QFileInfo &info = *itr;
        const QString &filePath = info.filePath();
        const QString &fileName = info.fileName();

        // 通知详情描述
        QString msg = tr("Extracting map file : ") + fileName;
        notifyDetail(msg);

        // 提取当前地图文件
        result = extractOneMap(filePath);
        if (!result)
            break;

        ++pos;
        ++itr;
    }

    return result && !mTerminate;
}

// 根据具体地图文件格式提取地图图片和遮挡图
bool MapExtractor::extractOneMap(const QString &filePath)
{
    QFileInfo info(filePath);
    mFileName = info.fileName();

    // 一次性读取文件所有内容到内存中处理
    QFile file(filePath);
    if (!file.open(QIODevice::ReadOnly))
    {
        QString msg = QString("Open file failed ! File : %1").arg(filePath);
        notifyCompleted(false, -1, msg);
        return false;
    }

    qint64 size = file.size();

    char *buffer = new char[size];
    file.read(buffer, size);
    file.close();

    mCurUnitIndex = 0;

    // 提取文件标识
    uint flag = 0;
    memcpy(&flag, buffer, sizeof(flag));
    char *data = buffer + sizeof(flag);
    size -= sizeof(flag);

    bool result = false;

    switch (flag)
    {
    case MAP_MAPX:    // MAPX
        mMapType = flag;
        result = extractMapInMAPX(data, size);
        break;
    case MAP_M1_0:    // M1.0
        mMapType = flag;
        result = extractMapInM1_0(data, size);
        break;
    case MAP_M2_5:    // M2.5
        mMapType = flag;
        result = extractMapInM2_5(data, size);
        break;
    case MAP_M3_0:    // M3.0
        mMapType = flag;
        result = extractMapInM3_0(data, size);
        break;
    case MAP_ROL0:    // ROL0
        mMapType = flag;
        result = extractMapInROL0(data, size);
        break;
    default:
        notifyCompleted(false, -1, "Unknown file format !");
        break;
    }

    return result;
}

// 提取MAPX格式地图文件
bool MapExtractor::extractMapInMAPX(const char *data, qint64 size)
{
    bool result = false;
    int pos = 0;
    const char *srcData = data;
    qint64 srcSize = 0;
    char *dstData = 0;
    qint64 dstSize = 0;

    // 读取地图宽度
    srcSize = sizeof(mMapWidth);
    memcpy(&mMapWidth, srcData, srcSize);
    srcData += srcSize;
    pos += srcSize;
    // 读取地图高度
    srcSize = sizeof(mMapHeight);
    memcpy(&mMapHeight, srcData, srcSize);
    srcData += srcSize;
    pos += srcSize;

    // 计算地图单元索引总数
    mSubMapWidth = 320;
    mSubMapHeight = 240;
    mSubMapCols = qCeil((qreal)(mMapWidth / mSubMapWidth));
    mSubMapRows = qCeil((qreal)(mMapHeight / mSubMapHeight));
    mSubMapCount = mSubMapRows * mSubMapCols;

    // 读取索引列表
    uint *indexList = new uint[mSubMapCount];
    srcSize = mSubMapCount * sizeof(uint);
    memcpy(indexList, srcData, srcSize);
    srcData += srcSize;
    pos += srcSize;

    // 读取地图文件大小
    uint fileSize = 0;
    srcSize = sizeof(fileSize);
    memcpy(&fileSize, srcData, srcSize);
    srcData += srcSize;
    pos += srcSize;

    // 读取JPGH文件头
    result = extractJPGH(srcData, size-pos, mJpgHeader, mJpgHeaderSize);

    if (!result)
    {
        delete []indexList;
        return result;
    }

    srcData += mJpgHeaderSize;
    pos += mJpgHeaderSize;

    mBmpData = new uchar[mSubMapWidth*mSubMapHeight*mSubMapCount*4];

    if (MAP_MAPX == mMapType)
    {
        // 目前暂时无法解出jpeg数据区数据，直接使用已经解出的jpg数据来辅助解遮挡数据
        result = loadXY2Image();
        if (!result)
        {
            delete []indexList;
            return result;
        }
    }

    removeMaskInfoFile();

    // 循环读取每个单元段
    int i = 0;
    for (i = 0; i < mSubMapCount; ++i)
    {
        mCurUnitIndex = i;
        pos = indexList[i] - 4;
        srcData = data + pos;
        result = extractUnitData(srcData, size-pos);
        if (!result)
            break;
    }

    delete []indexList;
    indexList = 0;

    if (result)
    {
        result = saveImageToFile();
    }

    delete []mJpgHeader;
    mJpgHeader = 0;

    delete []mBmpData;
    mBmpData = 0;

    return result;
}

// 提取M1.0格式地图文件
bool MapExtractor::extractMapInM1_0(const char *data, qint64 size)
{
    bool result = false;
    int pos = 0;
    const char *srcData = data;
    qint64 srcSize = 0;

    // 读取地图宽度
    srcSize = sizeof(mMapWidth);
    memcpy(&mMapWidth, srcData, srcSize);
    srcData += srcSize;
    pos += srcSize;
    // 读取地图高度
    srcSize = sizeof(mMapHeight);
    memcpy(&mMapHeight, srcData, srcSize);
    srcData += srcSize;
    pos += srcSize;

    // 计算地图单元索引总数
    mSubMapWidth = 320;
    mSubMapHeight = 240;
    mSubMapCols = qCeil((qreal)mMapWidth / (qreal)mSubMapWidth);
    mSubMapRows = qCeil((qreal)mMapHeight / (qreal)mSubMapHeight);
    mSubMapCount = mSubMapRows * mSubMapCols;

    mBmpData = new uchar[mSubMapWidth*mSubMapHeight*mSubMapCount*4];

    // 读取索引列表
    uint *indexList = new uint[mSubMapCount];
    srcSize = mSubMapCount * sizeof(uint);
    memcpy(indexList, srcData, srcSize);
    srcData += srcSize;
    pos += srcSize;

    // 跳过4字节未知数据
    srcData += 4;
    pos += 4;

    // 读取遮挡图数目
    int maskCount = 0;
    srcSize = sizeof(maskCount);
    memcpy(&maskCount, srcData, srcSize);
    srcData += srcSize;
    pos += srcSize;

    // 读取遮挡数据的偏移数据
    uint *maskOffsetList = 0;
    if (maskCount > 0)
    {
        maskOffsetList = new uint[maskCount];
        srcSize = maskCount * sizeof(uint);
        memcpy(maskOffsetList, srcData, srcSize);
        srcData += srcSize;
        pos += srcSize;
    }

    // 循环读取每个单元段
    int i = 0;
    for (i = 0; i < mSubMapCount; ++i)
    {
        mCurUnitIndex = i;
        pos = indexList[i] - 4;
        srcData = data + pos;
        result = extractUnitData(srcData, size-pos);
        if (!result)
            break;
    }

    delete []indexList;
    indexList = 0;

    if (result)
    {
        result = saveImageToFile();
    }

    removeMaskInfoFile();

    // 提取遮挡数据
    if (result && maskCount > 0)
    {
        int i = 0;
        for (i = 0; i < maskCount; ++i)
        {
            srcData = data + maskOffsetList[i] - 4;
            srcSize = size - maskOffsetList[i] - 4;
            pos = maskOffsetList[i];
            result = extractMaskData(srcData, srcSize, i);
            if (!result)
                break;
        }

        delete []maskOffsetList;
        maskOffsetList = 0;
    }

    delete []mBmpData;
    mBmpData = 0;

    return result;
}

// 提取M2.5格式地图文件
bool MapExtractor::extractMapInM2_5(const char *data, qint64 size)
{
    return true;
}

// 提取M3.0格式地图文件
bool MapExtractor::extractMapInM3_0(const char *data, qint64 size)
{
    return true;
}

// 提取ROL格式地图背景文件
bool MapExtractor::extractMapInROL0(const char *data, qint64 size)
{
    return true;
}

// 提取JPGH单元数据
bool MapExtractor::extractJPGH(const char *srcData, qint64 srcSize, char *&dstData, qint64 &dstSize)
{
    dstSize = srcSize - 8;
    dstData = new char[dstSize];
    memcpy(dstData, srcData+8, srcSize);
    return true;
}

// 提取地图块数据
bool MapExtractor::extractUnitData(const char *data, qint64 size)
{
    bool result = false;
    const char *srcData = data;
    qint64 srcSize = size;
    char *dstData = 0;
    qint64 dstSize = 0;

    bool loop = true;

    int maskCount = 0;
    memcpy(&maskCount, srcData, sizeof(maskCount));
    srcData += sizeof(maskCount);
    srcSize -= sizeof(maskCount);

    if (maskCount > 0)
    {
        Q_LOG_FORMAT(Q_LOG_LVL_DEBUG, "mask count = %d", maskCount);
        if (MAP_M1_0 == mMapType)
        {
            srcData += sizeof(uint) * maskCount;
            srcSize -= sizeof(uint) * maskCount;
        }
    }

    int maskIndex = 0;

    while (loop)
    {
        UnitHeader unitHeader = {0, 0};
        memcpy(&unitHeader, srcData, sizeof(UnitHeader));
        srcData += sizeof(UnitHeader);
        srcSize = unitHeader.size;

        switch (unitHeader.flag)
        {
        case UNIT_IMAG: // IMAG
            {
                result = extractImageData(srcData, srcSize, dstData, dstSize);
            }
            break;
        case UNIT_MSK2: // MSK2
            {
                result = extractMask2Data(srcData, srcSize, dstData, dstSize);
            }
            break;
        case UNIT_MASK: // MASK
            {
                result = extractMaskData(srcData, srcSize, maskIndex);
                maskIndex++;
            }
            break;
        case UNIT_JPEG: // JPEG
            {
                result = extractJPEGData(srcData, srcSize);
            }
            break;
        case UNIT_BLOK: // BLOK
            {
                result = extractBlockData(srcData, srcSize, dstData, dstSize);
            }
            break;
        case UNIT_CELL: // CELL
            {
                result = extractCellData(srcData, srcSize, dstData, dstSize);
            }
            break;
        case UNIT_BRIG: // BRIG
            {
                result = extractBrightData(srcData, srcSize, dstData, dstSize);
            }
            break;
        case UNIT_LIGT: // LIGT
            {
                result = extractLightData(srcData, srcSize, dstData, dstSize);
            }
            break;
        case UNIT_END:  // END
            {
                result = extractEndData(srcData, srcSize, dstData, dstSize);
                loop = false;
            }
            break;
        default:
            {
                loop = false;
                result = true;
            }
            break;
        }

        delete []dstData;
        dstData = 0;

        if (!result)
            break;

        srcData += srcSize;
    }

    return result;
}

// 提取IMAG单元数据
bool MapExtractor::extractImageData(const char *srcData, qint64 srcSize, char *&dstData, qint64 &dstSize)
{
    dstData = new char[srcSize];
    memcpy(dstData, srcData, srcSize);
    dstSize = srcSize;
    return true;
}

// 提取MSK2单元数据
bool MapExtractor::extractMask2Data(const char *srcData, qint64 srcSize, char *&dstData, qint64 &dstSize)
{
    dstData = new char[srcSize];
    memcpy(dstData, srcData, srcSize);
    dstSize = srcSize;
    return true;
}

// 提取MASK单元数据
bool MapExtractor::extractMaskData(const char *srcData, qint64 srcSize, int maskIndex)
{
    MaskHeader header;
    int offset = sizeof(MaskHeader);
    memcpy(&header, srcData, offset);

    const char *dstData = srcData + offset;
    int dstSize = srcSize - offset;
    if (MAP_M1_0 == mMapType)
    {
        memcpy(&dstSize, srcData+offset, sizeof(dstSize));
        dstData += sizeof(dstSize);
    }

    return extractMaskImage(dstData, dstSize, maskIndex, header);
}

// 提取JPEG单元数据
bool MapExtractor::extractJPEGData(const char *srcData, qint64 srcSize)
{
    bool result = false;

    if (MAP_MAPX == mMapType)
    {
//        dstData = new char[srcSize];
//        memcpy(dstData, srcData, srcSize);
//        dstSize = srcSize;
        result = true;
    }
    else
    {
        uchar *dstData = 0;
        qint64 dstSize = 0;
        result = extractStandardJPEGData((const uchar*)srcData, srcSize, dstData, dstSize);
        if (!result)
        {
            return result;
        }

        BMP *bmp = Unpak_jpg(dstData, dstSize);

        if (0 == bmp)
        {
            notifyCompleted(false, -1, "Unpack to standard jpg format failed !");
            return false;
        }

        result = fillBitmap(bmp->line[0], bmp->w, bmp->h, bmp->pitch);

        delete bmp;

        delete []dstData;
        dstData = 0;
    }

    return result;
}

// 提取BLOK单元数据;
bool MapExtractor::extractBlockData(const char *srcData, qint64 srcSize, char *&dstData, qint64 &dstSize)
{
    dstData = new char[srcSize];
    memcpy(dstData, srcData, srcSize);
    dstSize = srcSize;
    return true;
}

// 提取CELL单元数据
bool MapExtractor::extractCellData(const char *srcData, qint64 srcSize, char *&dstData, qint64 &dstSize)
{
    dstData = new char[srcSize];
    memcpy(dstData, srcData, srcSize);
    dstSize = srcSize;
    return true;
}

// 提取BRIG单元数据
bool MapExtractor::extractBrightData(const char *srcData, qint64 srcSize, char *&dstData, qint64 &dstSize)
{
    dstData = new char[srcSize];
    memcpy(dstData, srcData, srcSize);
    dstSize = srcSize;
    return true;
}

// 提取LIGT单元数据
bool MapExtractor::extractLightData(const char *srcData, qint64 srcSize, char *&dstData, qint64 &dstSize)
{
    dstData = new char[srcSize];
    memcpy(dstData, srcData, srcSize);
    dstSize = srcSize;
    return true;
}

// 提取END单元数据
bool MapExtractor::extractEndData(const char *srcData, qint64 srcSize, char *&dstData, qint64 &dstSize)
{
    dstData = new char[srcSize];
    memcpy(dstData, srcData, srcSize);
    dstSize = srcSize;
    return true;
}

bool MapExtractor::extractStandardJPEGData(const uchar *srcData, qint64 srcSize, uchar *&dstData, qint64 &dstSize)
{
    // JPEG数据处理原理
    // 1、复制D8到D9的数据到缓冲区中
    // 2、删除第3、4个字节 FFA0
    // 3、修改FFDA的长度00 09 为 00 0C
    // 4、在FFDA数据的最后添加00 3F 00
    // 5、替换FFDA到FF D9之间的FF数据为FF 00

    qint64 pos = 0;                         // 临时变量，表示已读取的长度
    uchar *tmpData = new uchar[srcSize*2];	// 临时变量，表示处理后的数据
    memset(tmpData, 0, srcSize*2);
    dstData = tmpData;               // 已处理数据的开始地址
    ushort count = 0;                       // 临时变量，表示循环的次数
    uint temp = 0;

    // 当已读取数据的长度小于总长度时继续
    while (pos < srcSize && *srcData++ == 0xFF)
    {
        *tmpData++ = 0xFF;
        pos++;
        switch (*srcData)
        {
        case 0xD8:
            *tmpData++ = 0xD8;
            *srcData++;
            pos++;
            break;
        case 0xA0:
            *srcData++;
            tmpData--;
            pos++;
            break;
        case 0xC0:
            *tmpData++=0xC0;
            *srcData++;
            pos++;

            memcpy(&count, srcData, sizeof(ushort)); // 读取长度
            swapByte(count); // 将长度转换为Intel顺序

            for (int i = 0; i< count; i++)
            {
                *tmpData++ = *srcData++;
                pos++;
            }

            break;
        case 0xC4:
            *tmpData++=0xC4;
            *srcData++;
            pos++;

            memcpy(&count, srcData, sizeof(ushort)); // 读取长度
            swapByte(count); // 将长度转换为Intel顺序

            for (int i = 0; i< count; i++)
            {
                *tmpData++ = *srcData++;
                pos++;
            }

            break;
        case 0xDB:
            *tmpData++ = 0xDB;
            *srcData++;
            pos++;

            memcpy(&count, srcData, sizeof(ushort)); // 读取长度
            swapByte(count); // 将长度转换为Intel顺序

            for(int i = 0;i< count; i++)
            {
                *tmpData++ = *srcData++;
                pos++;
            }
            break;
        case 0xDA:
            *tmpData++ = 0xDA;
            *tmpData++ = 0x00;
            *tmpData++ = 0x0C;
            *srcData++;
            pos++;

            memcpy(&count, srcData, sizeof(ushort)); // 读取长度
            swapByte(count); // 将长度转换为Intel顺序
            *srcData++;
            pos++;
            *srcData++;

            for(int i=2; i< count; i++)
            {
                *tmpData++ = *srcData++;
                pos++;
            }
            *tmpData++=0x00;
            *tmpData++=0x3F;
            *tmpData++=0x00;
            temp += 1; // 这里应该是+3的，因为前面的0xFFA0没有-2，所以这里只+1。

            // 循环处理0xFFDA到0xFFD9之间所有的0xFF替换为0xFF00
            while (pos < srcSize - 2)
            {
                if (*srcData == 0xFF)
                {
                    *tmpData++ = 0xFF;
                    *tmpData++ = 0x00;
                    *srcData++;
                    pos++;
                    temp++;
                }
                else
                {
                    *tmpData++ = *srcData++;
                    pos++;
                }
            }
            // 直接在这里写上了0xFFD9结束Jpeg图片.
            temp--; // 这里多了一个字节，所以减去。
            *tmpData--;
            *tmpData-- = 0xD9;
            break;
        case 0xD9:
            // 算法问题，这里不会被执行，但结果一样。
            *tmpData++ = 0xD9;
            pos++;
            break;
        default:
            break;
        }
    }

    temp += srcSize;
    dstSize = temp;

    return true;
}

bool MapExtractor::extractMaskImage(const char *srcData, qint64 srcSize, int maskIndex, const MaskHeader &header)
{
    // 解压mask数据
    int alignWidth = (header.w / 4 + (header.w % 4 != 0)) * 4;	// 以4对齐的宽度
    uchar* data = new uchar[alignWidth * header.h / 4];		// 1个字节4个像素，故要除以4
    int size = DecompressMask((char*)srcData, data);

    if (size <= 0)
    {
       QString msg = QString("解压遮挡数据失败！mask index = %1").arg(maskIndex);
       notifyCompleted(false, -1, msg);
       delete []data;
       return false;
    }

    // 输出mask数据到文件
    int numberOfPixel = header.w * header.h;
    uint *maskData = new uint[numberOfPixel];
    memset(maskData, 0, sizeof(uint) * numberOfPixel);

    int mapWidth = mSubMapCols * mSubMapWidth;
    int mapHeight = mSubMapRows * mSubMapHeight;
    uint *srcPixels = (uint*)mBmpData;

    int startX = mCurUnitIndex % mSubMapCols * mSubMapWidth;
    int startY = mCurUnitIndex / mSubMapCols * mSubMapHeight;

    if (mMapType != MAP_MAPX)
    {
        startX = 0;
        startY = 0;
    }

    // 提取mask像素
    for (int y = 0; y < header.h; y++)
    {
       for (int x = 0; x < header.w; x++)
       {
           int index = ((y * alignWidth + x) << 1);		// 单位:位
           int i = (index >> 3);
           uchar value = data[i];	// 定位到字节
           value = value >> (index & 7);	// 定位到位

           if ((value & 3) == 3)
           {
               int mapX = startX + header.x + x;	// 地图图像中的X位置
               int mapY = startY + header.y + y;	// 地图图像中的Y位置
               if (mapX >= 0 && mapY >= 0 && mapX < mapWidth && mapY < mapHeight)
               {
                   int srcIndex = (mapY) * mapWidth + mapX;
                   uint pixel = srcPixels[srcIndex];
                   int dstIndex = (y) * header.w + x;
                   maskData[dstIndex] = pixel;
               }
           }
       }
    }

    QString path = mOutputPath + mFileName + ".mask/";
    QDir dir(path);
    dir.mkpath(path);
    QString name = QString("%1%2_%3.png").arg(path).arg(mCurUnitIndex).arg(maskIndex);
    QImage image((uchar*)maskData, header.w, header.h, QImage::Format_ARGB32);
    image.save(name);

    delete []maskData;
    maskData = 0;

    // 输出描述信息到ini文件
    name = path + "mask_info.ini";
    QFile file(name);
    if (!file.open(QIODevice::Text|QIODevice::Append))
    {
       QString msg = QString("Open mask_info.ini failed ! File : %1").arg(name);
       notifyCompleted(false, -1, msg);
       return false;
    }

    QTextStream stream(&file);
    startX = startX + header.x;
    startY = startY + header.y;
    if (maskIndex == 0)
    {
       // 写ini段名称
       QString text = QString("[MaskInfo_%1]").arg(mCurUnitIndex, 3, 10, QChar('0'));
       stream<<text<<endl;
    }
    QString text = QString("mask_x_y_w_h_%1=%2,%3,%4,%5")
           .arg(maskIndex, 3, 10, QChar('0'))
           .arg(startX, 4, 10, QChar('0')).arg(startY, 4, 10, QChar('0'))
           .arg(header.w, 3, 10, QChar('0')).arg(header.h, 3, 10, QChar('0'));
    stream<<text<<endl;
    file.close();

    return true;
}

void MapExtractor::swapByte(ushort &value)
{
    ushort tempvalue = value >> 8;
    value = (value << 8) | tempvalue;
}

bool MapExtractor::fillBitmap(const ushort *data, int width, int height, int pitch)
{
    int row = mCurUnitIndex / mSubMapCols;
    int col = mCurUnitIndex % mSubMapCols;
    int startX = col * mSubMapWidth;
    int startY = row * mSubMapHeight;

    int dstWidth = mSubMapWidth * mSubMapCols;
    int dstHeight = mSubMapHeight * mSubMapRows;
    const ushort *src = data;
    uint *dst = ((uint*)mBmpData) + startY * dstWidth + startX;

    const ushort *srcLine = src;
    uint *dstLine = dst;

    for (int y = 0; y < height; y++)
    {
        for (int x = 0; x < width; x++)
        {
            ushort srcPixel = srcLine[x];
            dstLine[x] = R5G5B5_RGB(srcPixel);
        }

        srcLine += width;
        dstLine += dstWidth;
    }

    return true;
}

bool MapExtractor::saveImageToFile()
{
    // 写bmp数据到文件
    int bmpWidth = mSubMapCols * mSubMapWidth;
    int bmpHeight = mSubMapRows * mSubMapHeight;
    QString path = mOutputPath + mFileName + ".png";
    QImage image(mBmpData, bmpWidth, bmpHeight, QImage::Format_RGB32);
    bool result = image.save(path);
    if (!result)
    {
        QString msg = QString("Save map background image file failed ! File : %1").arg(path);
        notifyCompleted(false, -1, msg);
    }

    // 导出小图
    path = mOutputPath + mFileName + ".small/";
    QDir dir(path);
    dir.mkpath(path);
    int col = 0, row = 0;
    for (row = 0; row < mSubMapRows; ++row)
    {
        for (col = 0; col < mSubMapCols; ++col)
        {
            qint64 imgSize = mSubMapWidth * mSubMapHeight * 4;
            uchar *imgData = new uchar[imgSize];
            uchar *dst = imgData;
            int index = bmpWidth * mSubMapHeight * row + col * mSubMapWidth;
            uchar *src = mBmpData + index * 4;

            int i = 0;
            for (i = 0; i < mSubMapHeight; ++i)
            {
                memcpy(dst, src, mSubMapWidth * 4);
                dst += 4 * mSubMapWidth;
                src += 4 * bmpWidth;
            }

            QString name = QString("%1%2_%3.png").arg(path).arg(row).arg(col);
            QFile file(name);
            QImage *img = new QImage(imgData, mSubMapWidth, mSubMapHeight, QImage::Format_RGB32);
            bool result = img->save(name);
            if (!result)
            {
                QString msg = QString("Save map background small image file failed ! File : %1").arg(name);
                notifyCompleted(false, -1, msg);
            }

            delete img;
            delete []imgData;
        }
    }

    return result;
}

bool MapExtractor::loadXY2Image()
{
    QString path = mImagePath + mFileName + ".jpg";
    QImage image(path);

    int width = image.width();
    int height = image.height();
    int depth = image.depth();

    const uchar *data = image.bits();
    memcpy(mBmpData, data, image.byteCount());

    return true;
}

void MapExtractor::removeMaskInfoFile()
{
    QString fileName = mOutputPath + mFileName + ".mask/" + "mask_info.ini";
    QFile::remove(fileName);
}

void MapExtractor::notifyStarted(int total)
{
    emit started(total);
}

void MapExtractor::notifyProgress(int pos)
{
    emit progress(pos);
}

void MapExtractor::notifyDetail(const QString &text)
{
    emit detail(text);
}

void MapExtractor::notifyCompleted(bool finished, int errorCode, const QString &errorDesc)
{
    emit completed(finished, errorCode, errorDesc);
}

void MapExtractor::run()
{
    extract();
}

